﻿#include <QCoreApplication>
#include <QDebug>
#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <pthread.h> // 使用pthread_getattr_np()函数需要定义宏_GNU_SOURCE，而且要在pthread.h前定义
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // for sleep
#include <errno.h>
#include <limits.h>
#include <sched.h>
#include <signal.h>

using namespace std;

// Qt Creator非常智能，已经不需要在代码中手动添加pthread头文件和库了，就像一个内置线程库。

/// 线程函数
void* thfunc(void *arg)
{
    qDebug() << "in thfunc";
    return (void*)0;
}
/// 线程函数1
void* thfunc1(void *arg)
{
    int *pn = (int*)arg; // 获取参数地址
    int n = *pn;         // 解引
    qDebug() << QString("in thfunc:n=%1").arg(n);
    return (void*)0;
}
/// 线程函数2
void* thfunc2(void *arg)
{
    char *str;
    str = (char*)arg; // 得到传进来的字符串
    qDebug() << QString("in thfunc:str=%1").arg(str);
    return (void*)0;
}

typedef struct // 定义结构体的类型
{
    int n;
    char *str;
}MYSTRUCT;
/// 线程函数3
void* thfunc3(void *arg)
{
    MYSTRUCT *p = (MYSTRUCT*)arg; // 得到传进来的字符串
    qDebug() << QString("in thfunc:n=%1,str=%2").arg(p->n).arg(p->str); // 打印结构体的内容
    return (void*)0;
}

int gn = 10; // 定义一个全局变量，将会在主线程和子线程中用到
/// 线程函数4
void* thfunc4(void *arg)
{
    gn++; // 递增1
    qDebug() << QString("in thfunc:gn=%1").arg(gn); // 打印全局变量gn的值
    return (void*)0;
}

/// 线程函数5
void* thfunc5(void *arg)
{
    qDebug() << "sub thread is running";
    return NULL;
}

/// 输出自定义的错误信息
#define handle_error_en(en,msg) do {errno=en;perror(msg);exit(EXIT_FAILURE);} while (0)
static void* thread_start7(void *arg)
{
    int i,s;
    pthread_attr_t gattr; // 定义线程属性结构体
    s = pthread_getattr_np(pthread_self(),&gattr); // 获取当前线程属性结构值
    if(s!=0)
        handle_error_en(s,"pthread_getattr_np"); // 打印错误信息
    qDebug() << "Thread's detachstate attribute";
    s = pthread_attr_getdetachstate(&gattr,&i); // 从属性结构值中获取分离状态的属性
    if(s)
        handle_error_en(s,"pthread_attr_getdetachstate");
    qDebug() << QString("Detach state =%1").arg(
                    (i == PTHREAD_CREATE_DETACHED)?"PTHREAD_CREATE_DETACHED" :
                    (i == PTHREAD_CREATE_JOINABLE)?"PTHREAD_CREATE_JOINABLE" :
                    "???");
    pthread_attr_destroy(&gattr);
}

/// 线程函数8
static void* thread_start8(void *arg)
{
    int i,s;
    pthread_attr_t gattr; // 定义线程属性结构体
    s = pthread_getattr_np(pthread_self(),&gattr); // 获取当前线程属性结构值
    if(s!=0)
        handle_error_en(s,"pthread_getattr_np"); // 打印错误信息
    qDebug() << "Thread's detachstate attribute";
    s = pthread_attr_getdetachstate(&gattr,&i); // 从属性结构值中获取分离状态的属性
    if(s)
        handle_error_en(s,"pthread_attr_getdetachstate");
    qDebug() << QString("Detach state =%1").arg(
                    (i == PTHREAD_CREATE_DETACHED)?"PTHREAD_CREATE_DETACHED" :
                    (i == PTHREAD_CREATE_JOINABLE)?"PTHREAD_CREATE_JOINABLE" :
                    "???");
    pthread_detach(pthread_self()); // 把线程转换为可分离的线程
    s = pthread_getattr_np(pthread_self(),&gattr); // 获取当前线程属性结构值
    if(s!=0)
        handle_error_en(s,"pthread_getattr_np"); // 打印错误信息
    qDebug() << "Thread's detachstate attribute";
    s = pthread_attr_getdetachstate(&gattr,&i); // 从属性结构值中获取分离状态的属性
    if(s)
        handle_error_en(s,"pthread_attr_getdetachstate");
    qDebug() << QString("Detach state =%1").arg(
                    (i == PTHREAD_CREATE_DETACHED)?"PTHREAD_CREATE_DETACHED" :
                    (i == PTHREAD_CREATE_JOINABLE)?"PTHREAD_CREATE_JOINABLE" :
                    "???");
    pthread_attr_destroy(&gattr); // 销毁属性
}

/// 线程函数9
static void* thread_start9(void *arg)
{
    int i,res;
    size_t stack_size;
    pthread_attr_t gattr;
    res = pthread_getattr_np(pthread_self(),&gattr);
    if(res)
        qDebug() << "pthread_getattr_np failed";
    res = pthread_attr_getstacksize(&gattr,&stack_size);
    if(res)
        qDebug() << "pthread_attr_getstacksize failed";
    qDebug() << QString("Default stack size is %1 byte").arg(stack_size);
    //qDebug() << QString("minimum is %1 byte").arg(PTHREAD_STACK_SIZE);
}

#define PTHREAD_NUM 2
/// 线程函数10
void* thrfunc10(void *arg)
{
    static int count = 1; // 这里需要是静态变量
    pthread_exit((void*)(&count)); // 通过pthread_exit结束线程
}
/// 线程函数11
void* thrfunc11(void *arg)
{
    static int count = 2; // 这里需要是静态变量
    return ((void*)(&count)); // 线程函数返回
}

/// 信号处理函数
static void on_signal_term(int sig)
{
    qDebug() << "sub thread will exit";
    pthread_exit(NULL);
}
#define SIGQUIT 3
void* thfunc12(void *arg)
{
    signal(SIGQUIT,on_signal_term); // 注册信号处理函数
    int tm = 50;
    while(true)
    {
        qDebug() << QString("thfunc12--left:%1 s--").arg(tm);
        sleep(1);
        tm --;
    }
    return (void*)0;
}
/// 线程函数13
void* thfunc13(void *arg)
{
    int tm = 50;
    while(tm > 48) // 当tm递减到小于等于48时，循环退出
    {
        qDebug() << QString("thfunc12--left:%1 s--").arg(tm);
        sleep(1);
        tm --;
    }
    return (void*)0;
}

/// 线程函数14
void* thfunc14(void *arg)
{
    int i = 1;
    qDebug() << "thread start--------";
    while(1)
        i++;
    return (void*)0;
}
/// 线程函数15
void* thfunc15(void *arg)
{
    int i = 1;
    qDebug() << "thread start--------";
    while(1)
    {
        i++;
        pthread_testcancel(); // 让系统测试取消请求
    }
    return (void*)0;
}

/// 清理函数
void mycleanfunc16(void *arg)
{
    qDebug() << QString("mycleanfunc:%1").arg(*(int*)arg); // 打印传进来的参数
}
void* thfunc16(void *arg)
{
    int m = 1;
    qDebug() << "thfunc16 comes";
    pthread_cleanup_push(mycleanfunc16,&m); // 把清理函数压栈
    return (void*)0; // 退出线程
    pthread_cleanup_pop(0); // 清理函数弹出栈，虽不会执行，但必须有，否则编译不通过
}
void* thfunc17(void *arg)
{
    int m = 2;
    qDebug() << "thfunc17 comes";
    pthread_cleanup_push(mycleanfunc16,&m); // 把清理函数压栈
    pthread_exit(0); // 退出线程
    pthread_cleanup_pop(0); // 清理函数弹出栈，虽不会执行，但必须有，否则编译不通过
}
void* thfunc18(void *arg)
{
    int m = 1, n = 2;
    qDebug() << "thfunc18 comes";
    pthread_cleanup_push(mycleanfunc16,&m); // 把清理函数压栈
    pthread_cleanup_push(mycleanfunc16,&n); // 再压栈一个清理函数
    pthread_cleanup_pop(1); // 出栈清理函数，并执行（最后压入的最先出栈）
    pthread_exit(0); // 退出线程
    pthread_cleanup_pop(0); // 不会执行，仅仅为了成对
}
void* thfunc19(void *arg)
{
    int i = 1;
    qDebug() << "thread start--------";
    pthread_cleanup_push(mycleanfunc16,&i); // 把清理函数压栈
    while(1)
    {
        i++;
        qDebug() << QString("i=%1").arg(i);
    }
    qDebug() << "this line will not run"; // 这句不会调用
    pthread_cleanup_pop(0); // 仅仅为了成对出现
    return (void*)0;
}


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 创建一个简单的线程，不传参数
    pthread_t tidp;
    int ret;
    ret = pthread_create(&tidp,NULL,thfunc,NULL); // 创建线程
    if(ret)
    {
        qDebug() << QString("pthread_create failed:%1").arg(ret);
        return -1;
    }
    sleep(1); // main线程挂起1秒，为了让子线程有机会去执行
    qDebug() << "in main:thread is created";

    // 创建一个线程，并传入整数类型的参数
    pthread_t tidp1;
    int ret1,n1=110;
    ret1 = pthread_create(&tidp1,NULL,thfunc1,&n1); // 创建线程并传递n1的地址
    if(ret1)
    {
        qDebug() << QString("pthread_create failed:%1").arg(ret1);
        return -1;
    }
    pthread_join(tidp1,NULL); // 等待子线程结束
    qDebug() << "in main:thread is created";

    // 创建一个线程，并传递字符串作为参数
    pthread_t tidp2;
    int ret2;
    const char *str2="hello world";
    ret2 = pthread_create(&tidp2,NULL,thfunc2,(void*)str2); // 创建线程并传递str2的地址
    if(ret2)
    {
        qDebug() << QString("pthread_create failed:%1").arg(ret2);
        return -1;
    }
    pthread_join(tidp2,NULL); // 等待子线程结束
    qDebug() << "in main:thread is created\n";

    // 创建一个线程，并传递结构体作为参数
    pthread_t tidp3;
    int ret3;
    MYSTRUCT mystruct3; // 定义结构体
    // 初始化结构体
    mystruct3.n = 1100;
    mystruct3.str = "hello world!";
    ret3 = pthread_create(&tidp3,NULL,thfunc3,(void*)&mystruct3); // 创建线程并传递mystruct3结构体的地址
    if(ret3)
    {
        qDebug() << QString("pthread_create failed:%1").arg(ret3);
        return -1;
    }
    pthread_join(tidp3,NULL); // 等待子线程结束
    qDebug() << "in main:thread is created\n";

    // 创建一个线程，共享进程数据
    pthread_t tidp4;
    int ret4;
    ret4 = pthread_create(&tidp4,NULL,thfunc4,NULL); // 创建线程
    if(ret4)
    {
        qDebug() << QString("pthread_create failed:%1").arg(ret4);
        return -1;
    }
    pthread_join(tidp4,NULL); // 等待子线程结束
    gn++; // 子线程结束后，gn再递增1
    qDebug() << QString("in main:gn=%1").arg(gn); // 再次打印全局变量gn的值

    // 创建一个可分离线程
    /* 分离状态（Detached State）是线程很重要的一个属性。POSIX线程的分离状态决定一个线程以什么样的方式来终止自己。
     * 这里所说的分离状态是POSIX标准下的属性所特有的，用于表明该线程以何种方式终止自己。默认的分离状态是可连接的，即创建线程时如果使用默认属性，
     *   则分离状态属性就是可连接的，因此默认属性下创建的线程是可连接的线程。
     * POSIX下的线程要么是分离的，要么是非分离的（也称可连接的，joinable）。前者用宏PTHREAD_CREATE_DETACHED表示，
     *   后者用宏PTHREAD_CREATE_JOINABLEB表示。默认情况下创建的线程是可连接的，一个可连接的线程可以被其他线程收回资源和杀死（或称撤销），
     *   并且不会主动释放资源（比如堆栈空间），必须等待其他线程来回收它占用的资源，因此我们要在主线程中调用pthread_join()函数（阻塞函数，
     *   当它返回时所等待的线程的资源就被释放了）。
     * 如果是可连接的线程，那么线程函数自己返回结束时或调用pthread_exit()结束时都不会释放线程所占用的堆栈和线程描述符（总计8KB多），
     *   必须调用pthread_join()且返回后才会释放这些资源。这对于父进程长时间运行的进程来说会是灾难性的。
     *   因为父进程不退出并且没有调用pthread_join()，则这些可连接线程的资源就一直不会释放，相当于变成僵尸线程，僵尸线程越来越多，
     *   再想创建新线程时将没有资源可用！
     * 重要的事情再说一遍，一个可连接的线程所占用的内存仅当有线程对其执行pthread_join()后才会释放，因此为了避免内存泄漏，
     *   可连接的线程在终止时，要么已被设为DETACHED（可分离），要么调用pthread_join()函数来回收资源。
     *   另外，一个线程不能被多个线程等待，否则第一个接收到信号的线程成功返回，其余调用pthread_join()的线程将得到错误代码ESRCH。
     * 可分离的线程。这种线程运行结束时，它的资源将会立刻被系统回收。可以这样理解，这种线程是能独立（分离）出去的，可以自生自灭，父线程不用管它。
     * 一个线程设置为可分离状态有两种方法：一种方法是调用函数pthread_detach()，它可以将线程转换为可分离线程；
     *   另一种方法是在创建线程时就将它设置为可分离状态，基本过程是首先初始化一个线程属性的结构体变量（通过函数pthread_attr_init()），
     *   然后将它设置为可分离状态（通过函数pthread_attr_setdetachstate()），最后将该结构体变量的地址作为参数传入线程创建函数 pthread_create()，
     *   这样所创建出来的线程就直接处于可分离状态了。
    */
    pthread_t thread_id5;
    pthread_attr_t thread_attr5;
    struct sched_param thread_param5;
    size_t stack_size5;
    int res5;
    res5 = pthread_attr_init(&thread_attr5);
    if(res5)
        qDebug() << "pthread_attr_init failed:" << res5;
    res5 = pthread_attr_setdetachstate(&thread_attr5,PTHREAD_CREATE_DETACHED);
    if(res5)
        qDebug() << "pthread_attr_setdetachstate failed:" << res5;
    res5 = pthread_create(&thread_id5,&thread_attr5,thfunc5,NULL);
    if(res5)
        qDebug() << "pthread_create failed:" << res5;
    qDebug() << "main thread will exit";
    sleep(1);

    // 创建一个可分离的线程，且main线程先退出
    /* 对于可连接的线程，主线程可以调用pthread_join()函数等待子线程结束。
     * 对于可分离线程，并没有这样的函数，但是可以先让主线程退出而进程不退出，一直等到子线程退出了才退出进程。
     * 也就是说，在主线程中调用函数pthread_exit()，如果在main线程中调用了pthread_exit()，那么此时终止的只是main线程，
     *   而进程的资源会为由main线程创建的其他线程保持打开的状态，直到其他线程都终止。
     *   值得注意的是，如果在非main线程（其他子线程）中调用pthread_exit()，则不会有这样的效果，只会退出当前子线程。
    */
    pthread_t thread_id6;
    pthread_attr_t thread_attr6;
    struct sched_param thread_param6;
    size_t stack_size6;
    int res6;
    res6 = pthread_attr_init(&thread_attr6); // 初始化线程结构体
    if(res6)
        qDebug() << "pthread_attr_init failed:" << res6;
    res6 = pthread_attr_setdetachstate(&thread_attr6,PTHREAD_CREATE_DETACHED); // 设置分离状态
    if(res6)
        qDebug() << "pthread_attr_setdetachstate failed:" << res6;
    res6 = pthread_create(&thread_id6,&thread_attr6,thfunc5,NULL); // 创建一个可分离的线程
    if(res6)
        qDebug() << "pthread_create failed:" << res6;
    qDebug() << "main thread will exit";
    pthread_exit(NULL); // 主线程退出，但进程不会此刻退出，下面的语句不会再执行
    qDebug() << "main thread has exited, this line will not run"; // 此句不会执行
    /* 正如我们预料的那样，在main线程中调用了函数pthread_exit()后将退出main线程，但进程并不会在此刻退出，而是等到子线程结束后才退出。
     *   因为是分离的线程，所以它结束的时候所占用的资源会立刻被系统回收。如果是一个可连接的（joinable）线程，
     *   则必须在创建它的线程中调用 pthread_join()函数来等待可连接线程的结束并释放该线程占用的资源。
     * 在任何一个时间点上，线程是可连接的（Joinable），或者是分离的（Detached）。一个可连接的线程在自己退出或pthread_exit()时
     *   都不会释放线程所占用堆栈和线程描述符（总计8KB多），需要通过其他线程调用pthread_join()之后才释放这些资源；
     *   一个分离的线程是不能被其他线程回收或杀死的，所占的资源在它终止时由系统自动释放。
    */

    // 获取线程的分离状态属性
    pthread_t phr7;
    int s7;
    s7 = pthread_create(&phr7,NULL,&thread_start7,NULL); // 创建线程
    if(s7!=0)
    {
        handle_error_en(s7,"pthread_create");
        return 0;
    }
    pthread_join(phr7,NULL); // 等待子线程结束

    // 把可连接的线程转为可分离的线程
    pthread_t thread_id8;
    int s8;
    s8 = pthread_create(&thread_id8,NULL,&thread_start8,NULL);
    if(s8!=0)
    {
        qDebug() << "pthrad_create failed";
        return 0;
    }
    pthread_exit(NULL); // 主线程退出，但进程并不马上结束

    // 获得线程默认的堆栈尺寸和最小尺寸
    pthread_t thread_id9;
    int s9;
    s9 = pthread_create(&thread_id9,NULL,&thread_start9,NULL);
    if(s9!=0)
    {
        qDebug() << "pthrad_create failed";
        return 0;
    }
    pthread_join(thread_id9,NULL); // 等待子线程结束

    /* Linux虽然是一个非实时操作系统，但是它的线程也有实时和分时之分，具体的调度策略可以分为3种：
     *   SCHED_OTHER（分时调度策略）、SCHED_FIFO（先来先服务调度策略）、SCHED_RR（实时的分时调度策略）。我们创建线程的时候可以指定其调度策略。
     *   默认的调度策略是SCHED_OTHER。SCHED_FIFO和SCHED_RR只用于实时线程。
     * SCHED_OTHER
     *   SCHED_OTHER表示分时调度策略（也可称轮转策略），是一种非实时调度策略，系统会为每个线程分配一段运行时间，称为时间片。
     *   该调度策略不支持线程优先级，无论获取该调度策略下的最高、最低优先级都是0。该调度策略有点像排队上买票，
     *   前面的人占用了位置，后一个人是轮不上的，而且也不能强行占用（不支持优先级，没有VIP特权之说）。
     * SCHED_FIFO
     *   SCHED_FIFO表示先来先服务调度策略，是一种实时调度策略，支持优先级抢占，可以算是一种实时调度策略。
     *   在SCHED_FIFO策略下，CPU按照创建线程的先后让一个先来的线程执行完再调度下一个线程。线程一旦占用CPU就会一直运行，直到有更高优先级的任务到达或原线程放弃。
     *   如果有和正在运行的线程具有同样优先级的线程就绪，则必须等待正在运行的线程主动放弃后才可以占用CPU投入运行。在SCHED_FIFO策略下，可设置的优先级范围是1到99。
     * SCHED_RR
     *   SHCED_RR表示时间片轮转（轮询）调度策略，但支持优先级抢占，因此也是一种实时调度策略。
     *   在SHCED_RR策略下，CPU会分配给每个线程一个特定的时间片，当线程的时间片用完时，系统将重新分配时间片，并将线程置于实时线程就绪队列的尾部，
     *   这样即可保证所有具有相同优先级的线程能够被公平地调度。
    */
    // 获取线程在3种调度策略下可设置的最低和最高优先级
    qDebug() << QString("Valid priority range for SCHED_OTHER:%1-%2")
                .arg(sched_get_priority_min(SCHED_OTHER)).arg(sched_get_priority_max(SCHED_OTHER));
    qDebug() << QString("Valid priority range for SCHED_FIFO:%1-%2")
                .arg(sched_get_priority_min(SCHED_FIFO)).arg(sched_get_priority_max(SCHED_FIFO));
    qDebug() << QString("Valid priority range for SCHED_RR:%1-%2")
                .arg(sched_get_priority_min(SCHED_RR)).arg(sched_get_priority_max(SCHED_RR));
    /* 对于SCHED_FIFO和SHCED_RR调度策略，由于支持优先级抢占，因此具有高优先级的可运行（就绪状态下的）线程总是先运行。
     *   如果出现一个更高优先级的线程就绪，那么正在运行的线程就可能在自己的CPU时间片未用完之前就被抢占了，
     *   甚至会在未开始其时间片前就被抢占了，而要按照调度策略等待下一次被选择运行。
     *   当Linux系统进行线程切换的时候，将执行一个上下文转换的操作，所谓上下文转换就是保存正在运行线程的相关状态，加载另一个线程的状态，开始新线程的执行。
     * 需要说明的是，虽然Linux支持实时调度策略（比如SCHED_FIFO和SCHED_RR），但是它依旧属于非实时操作系统，
     *   这是因为实时操作系统对响应时间有非常严格的要求，而Linux作为一个通用操作系统达不到这一要求（通用操作系统要求能支持一些响应速度较差的硬件，
     *   从硬件角度就达不到实时要求）。此外，Linux的线程优先级是动态的，也就是说即使高优先级线程还没有完成，低优先级的线程还是会得到一定的时间片.
     * 宇宙飞船常用的操作系统VxWorks就是一个RTOS（Real-Time Operating System，实时操作系统）。
    */

    /* 一般情况下，进程中各个线程的运行是相互独立的，线程的终止并不会相互通知，也不会影响其他的线程。
     *   对于可连接的线程，它终止后所占用的资源并不会随着线程的终止而归还给系统，而是仍为线程的其他进程持有，可以调用pthread_join()函数来同步并释放资源。
     * 线程主动结束，一般就是在线程函数中使用return语句或调用pthread_exit()函数。
     *   函数pthread_exit()的原型声明如下：void pthread_exit(void *retVal);
     *   其中，参数retval是线程退出时返回给主线程的值，线程函数的返回类型是void*。
    */
    // 线程终止并得到线程的退出码
    pthread_t pid10[PTHREAD_NUM]; // 定义两个线程id
    int retPid10;
    int *pRet10; // 注意这里是指针
    int *pRet11;
    if((retPid10 = pthread_create(&pid10[0],NULL,thrfunc10,NULL)) != 0)
    {
        perror("create pid first failed");
        return -1;
    }
    if((retPid10 = pthread_create(&pid10[1],NULL,thrfunc11,NULL)) != 0)
    {
        perror("create pid second failed");
        return -1;
    }
    if(pid10[0] != 0)
    {
        // 注意pthread_join函数的第二个参数的用法
        // 接收返回值的函数pthread_join()有两个作用：其一是等待线程结束；其二是获取线程结束时的返回值。
        // pthread_join()函数的第二个参数类型是void**二级指针，所以我们把整型指针pRet1的地址（int**类型）赋给它，再显式地转换为void**。
        pthread_join(pid10[0],(void**)&pRet10);
        qDebug() << QString("get thread 0 exitcode:%1").arg(*pRet10); // 打印线程返回值
    }
    if(pid10[1] != 0)
    {
        // 注意pthread_join函数的第二个参数的用法
        pthread_join(pid10[1],(void**)&pRet11);
        qDebug() << QString("get thread 1 exitcode:%1").arg(*pRet11); // 打印线程返回值
    }

    /* 线程被动结束，某个线程可能在执行一项耗时的计算任务，而用户没有耐心，希望结束该线程，此时线程要被动地结束。
     *   如何被动结束呢？一种方法是在同进程的另外一个线程中通过函数pthread_kill()发送信号给要结束的线程，目标线程收到信号后退出；
     *   另外一种方法是在同进程的其他线程中通过函数pthread_cancel()来取消目标线程的执行。
     * 向线程发送信号的函数是pthread_kill()，注意它不是杀死（kill）线程，而是向线程发信号，
     *   因此线程之间交流信息可以用这个函数，需要注意的是，接收信号的线程必须先用sigaction()函数注册该信号的处理函数。
     * 向指定ID的线程发送signal信号，如果线程代码内不进行处理，则按照信号默认的行为影响整个进程，
     *   也就是说，如果给一个线程发送了SIGQUIT，但线程没有实现signal的处理函数，则整个进程退出。
     *   所以，如果int sig的参数不是0，那么一定要清楚到底要干什么，最好要实现线程的信号处理函数，否则就会影响整个进程。
    */
    // 向线程发送请求结束信号
    pthread_t pid12;
    int res12;
    res12 = pthread_create(&pid12,NULL,thfunc12,NULL); // 创建子线程
    sleep(5); // 让出CPU5秒，让子线程执行
    // 5秒结束后，开始向子线程发送SIGQUIT信号，通知其结束
    pthread_kill(pid12,SIGQUIT);
    pthread_join(pid12,NULL); // 等待子线程结束
    qDebug() << "sub thread has completed, main thread will exit";

    // 判断线程是否已经结束
    pthread_t pid13;
    int res13;
    res13 = pthread_create(&pid13,NULL,thfunc13,NULL); // 创建线程
    sleep(5);
    int kill_rc = pthread_kill(pid13,0); // 发送信号0，探测线程是否存活
    if(kill_rc == ESRCH)
        qDebug() << "the specified thread did not exit or already quit";
    else if(kill_rc == EINVAL)
        qDebug() << "signal is invalid";
    else
        qDebug() << "the specified thread is alive";

    /* 除了通过函数pthread_kill()发送信号来通知线程结束，我们还可以通过函数pthread_cancel()来取消某个线程的执行。
     *   所谓取消某个线程的执行，是指发送取消请求，请求线程终止运行。注意，就算发送成功也不一定意味着线程就会停止运行。
     * 发送取消请求成功并不意味着目标线程就立即停止运行了，即系统并不会马上关闭被取消线程，
     *   只有在被取消线程下次调用一些系统函数或C库函数（比如printf）或者调用函数pthread_testcancel()
     *   让内核去检测是否需要取消当前线程时，才会真正结束线程。这种在线程执行过程中检测是否有未响应取消信号的地方叫作取消点，
     *   常见的取消点有printf、pthread_testcancel、read/write、sleep等函数调用的地方。
     *   如果取消线程成功了，就将自动返回常数PTHREAD_CANCELED（这个值是-1），可以通过pthread_join()函数获得这个退出值。
     * 可别小看了pthread_testcancel()函数，它可以在线程处于死循环中时让系统（内核）有机会去检查是否有取消请求发送过来，
     *   如果不调用pthread_testcancel()函数，则函数pthread_cancel()取消不了目标线程。
    */
    // 取消线程失败
    void *ret14 = NULL;
    pthread_t pid14;
    pthread_create(&pid14,NULL,thfunc14,NULL); // 创建线程
    sleep(1);
    pthread_cancel(pid14); // 发送取消线程的请求
    pthread_join(pid14,&ret14);
    if(ret14 == PTHREAD_CANCELED) // 判断是否成功取消线程
        qDebug() << QString("thread has stoped, and exit code:%1").arg(*(int*)ret14);
    else
        qDebug() << "some error occured";
    // 取消线程成功
    void *ret15 = NULL;
    pthread_t pid15;
    pthread_create(&pid15,NULL,thfunc15,NULL); // 创建线程
    sleep(1);
    pthread_cancel(pid15); // 发送取消线程的请求
    pthread_join(pid15,&ret15);
    if(ret15 == PTHREAD_CANCELED) // 判断是否成功取消线程
        qDebug() << QString("thread has stoped, and exit code:%1").arg(*(int*)ret15);
    else
        qDebug() << "some error occured";
    /* 我们在while死循环中添加了函数pthread_testcancel()，让系统每次循环都去检查一下有没有取消请求到来。
     *   不调用pthread_testcancel()函数的话，也可以在while循环中用sleep()函数来代替，
     *   但是这样会影响while的速度。在实际开发中，应该根据具体项目来选择使用。
    */

    /* 前面讲了线程的结束，其中主动结束可以认为是线程正常终止，是可预见的；被动结束是其他线程要求其结束，是不可预见的，是一种异常终止。
     *   不论是可预见的线程终止还是异常终止，都会存在资源释放的问题。
     * 如何保证线程终止时顺利地释放掉自己所占用的资源（特别是加锁资源）是一个必须要考虑解决的问题。
     *   最经常出现的情形是资源独占锁的使用：线程为了访问临界资源而为其加上锁，但在访问过程中线程被外界取消了，如果取消成功，则该临界资源将永远处于锁定状态而得不到释放。
     *   外界取消线程的操作是不可预见的，因此的确需要一个机制来简化用于资源释放的编程，也就是需要一个在线程退出时执行清理操作的机会。
     * POSIX线程库提供了函数pthread_cleanup_push()和pthread_cleanup_pop()，用于让线程退出时执行一些清理操作。
     *   这两个函数采用先入后出的堆栈结构进行管理，前者用于把一个函数压入清理函数栈，后者用于弹出栈顶的清理函数，并根据参数来决定是否执行清理函数。
     *   多次调用函数pthread_cleanup_push()将把当前在栈顶的清理函数往下压，弹出清理函数时在栈顶的清理函数将先被弹出.
     * 用pthread_cleanup_push()压栈的清理函数在下面3种情况下会执行：
     *   （1）线程主动结束时，比如return或调用pthread_exit()函数。
     *   （2）调用函数pthread_cleanup_pop()，且它的参数为非0时。
     *   （3）线程被其他线程取消时，也就是有其他线程对该线程调用了pthread_cancel()函数。
     * pthread_cleanup_pop()函数必须和pthread_cleanup_push()函数成对出现在同一个函数中，否则编译通不过。
    */
    // 线程主动结束时候调用清理函数
    pthread_t pid16,pid17;
    int res16,res17;
    res16 = pthread_create(&pid16,NULL,thfunc16,NULL); // 创建线程16
    if(res16)
    {
        qDebug() << QString("pthread_create faild:%1").arg(strerror(res16));
        exit(1);
    }
    pthread_join(pid16,NULL); // 等待线程16结束
    res17 = pthread_create(&pid17,NULL,thfunc17,NULL); // 创建线程16
    if(res17)
    {
        qDebug() << QString("pthread_create faild:%1").arg(strerror(res17));
        exit(1);
    }
    pthread_join(pid17,NULL); // 等待线程17结束
    // pthread_cleanup_pop()函数调用清理函数
    pthread_t pid18;
    int res18;
    res18 = pthread_create(&pid18,NULL,thfunc18,NULL); // 创建线程
    if(res18)
    {
        qDebug() << QString("pthread_create faild:%1").arg(strerror(res18));
        exit(1);
    }
    pthread_join(pid18,NULL); // 等待线程结束
    qDebug() << "main over";
    // 取消线程时引发清理函数的执行
    pthread_t pid19;
    int iret19 = 0;
    void *ret19 = NULL;
    pthread_create(&pid19,NULL,thfunc19,NULL); // 创建线程
    sleep(1); // 等一会，让子线程开始while循环
    pthread_cancel(pid19); // 发送取消线程的请求
    pthread_join(pid19,&ret19); // 等待线程结束
    if(ret19 == PTHREAD_CANCELED)
        qDebug() << QString("thread has stopped, and exit code:%1").arg(*(int*)ret19); // 打印返回值，应该是-1
    else
        qDebug() << "some error occurs";



    return a.exec();
}
